package com.joy.web.admin.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.joy.common.constant.PermissionCons;
import com.joy.web.admin.mapper.ResourceMapper;
import com.joy.common.constant.CommonCons;
import com.joy.common.model.vo.ResourceVO;
import com.joy.web.admin.entity.Resource;
import com.joy.web.admin.service.IResourceService;
import com.joy.web.base.service.impl.IBaseServiceImpl;
import lombok.extern.slf4j.Slf4j;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author yjzhao
 * @since 2019-09-16
 */
@Service
@Slf4j
public class ResourceServiceImpl extends IBaseServiceImpl<ResourceMapper, Resource> implements IResourceService {

    @Override
    public List<ResourceVO> getResourceTree() throws Exception {
        QueryWrapper<Resource> resourceQ = new QueryWrapper<>();
        resourceQ.lambda()
                .eq(Resource::getDelYn, CommonCons.FlagType.N)
                .eq(Resource::getIsShow, 1);

        /** 获取当前可用的 资源列表 */
        List<Resource> resourceList = this.list(resourceQ);
        List<ResourceVO> resourceTree = Lists.newArrayList();
        resourceList.stream()
                .filter(resource -> resource.getParentId().equals("0"))
                .sorted(Comparator.comparing(Resource::getSort))
                .forEach(nowResource -> {
                    ResourceVO resourceVO = nowResource.convert(ResourceVO.class);
                    List<ResourceVO> childrenMenuList = loopResourceTree(resourceVO, resourceList);
                    resourceVO.setChildsResourceList(childrenMenuList);
                    resourceTree.add(resourceVO);
                });

        return resourceTree;
    }

    @Override
    @Cacheable(value=CommonCons.MyCache.PERMISSIONS, key="#userNm")
    public Set<String> getPermissionsByUserNm(String userNm) throws Exception {
        Set<String> permissionSet = Sets.newHashSet();
        List<String> permissionsList = baseMapper.getPermissionsByUserNm(userNm);
        permissionsList.stream()
                .filter(permission -> StrUtil.isNotEmpty(permission))
                .forEach(permission -> {
                        Stream.of(permission.split(","))
                                .filter(subPermission -> StrUtil.isNotEmpty(subPermission))
                                .forEach(p ->{
                                    permissionSet.add(p);
                                });
                });

        permissionSet.addAll(PermissionCons.UN_PERMISSION_SET);


        // 加上菜单
        List<Resource> menuList = baseMapper.getResourceMenuByUserNm(userNm);
        menuList.stream().forEach(resource -> {
            String url = resource.getUrl();
            if (StrUtil.isNotEmpty(url)) {
                permissionSet.add("menu:" + url.replaceFirst("/", "").replace("/", ":"));
            }
        });
        return permissionSet;
    }

    @Override
    @Cacheable(value = CommonCons.MyCache.MENUTREE, key = "#userNm")
    public List<ResourceVO> getMenuTreeByUserNm(String userNm) throws Exception {
        List<Resource> menuList = baseMapper.getResourceMenuByUserNm(userNm);
        if (CollectionUtils.isEmpty(menuList)) {
            return null;
        }
        List<ResourceVO> resourceTree = Lists.newArrayList();
        menuList.stream()
                .filter(resource -> resource.getParentId().equals("0"))
                .filter(resource -> {
                    long count = resourceTree.stream()
                            .filter(resourceTreeSub -> resource.getId().equals(resourceTreeSub.getId()))
                            .count();
                    if (count > 0) {
                        return false;
                    }
                    return true;
                })
                .forEach(nowResource -> {
                    ResourceVO resourceVO = nowResource.convert(ResourceVO.class);
                    List<ResourceVO> childrenMenuList = loopResourceTree(resourceVO, menuList);
                    resourceVO.setChildsResourceList(childrenMenuList);
                    resourceTree.add(resourceVO);
                });


        //根据 sort 属性排序
        return resourceTree.stream()
                .filter(resourceTreeSub -> resourceTreeSub.getSort() != null)
                .sorted(Comparator.comparing(ResourceVO::getSort))
                .collect(Collectors.toList());
    }

    @Override
    public void initAllResourceToCache() throws Exception {
        //全部 menu 缓存
        QueryWrapper<Resource> resourceQ = new QueryWrapper<>();
        resourceQ.lambda()
                .eq(Resource::getDelYn, CommonCons.FlagType.N)
                .eq(Resource::getIsShow, 1)
                .eq(Resource::getType, CommonCons.resourceTyp.MENU);

        List<Resource> menuList = this.list(resourceQ);
        //用于粗糙日志记录
        Cache menuUrlCache = CacheManager.getInstance().getCache(CommonCons.MyCache.MENU_URLS);
        menuUrlCache.removeAll();

        //所有路径信息记录，方便查询 code 和 菜单路径
        Cache actionLikeKeyCache = CacheManager.getInstance().getCache(CommonCons.MyCache.ACTION_LIKE_KEYS);
        actionLikeKeyCache.removeAll();

        for(Resource resource : menuList){
            if(StrUtil.isEmpty(resource.getUrl()) || resource.getUrl().equals("#")) {
                continue;
            }
            Element menuUrlEle = new Element(resource.getUrl(), resource.getResourceName());
            menuUrlCache.put(menuUrlEle);

            LinkedList<Resource> link = Lists.newLinkedList();
            link.addFirst(resource);
            if(!resource.getParentId().equals("0")) {
                putPraentResource(menuList, resource.getParentId(), link);
            }
            Map<String, Object> recordList = Maps.newHashMap();
            recordList.put("code", resource.getCode());
            recordList.put("links", link);

            Element actionLikeKeyEle = new Element(resource.getUrl(), recordList);
            actionLikeKeyCache.put(actionLikeKeyEle);
        }
    }

    /**
     * 迭代权限树
     * @param fatherResource
     * @param menuList
     */
    private List<ResourceVO> loopResourceTree(ResourceVO fatherResource, List<Resource> menuList) {
        if (CollectionUtils.isEmpty(menuList)) {
            return null;
        }
        List<ResourceVO> resourceTree = Lists.newArrayList();
        List<Resource> childenResourceList = menuList.stream()
                .filter(nowResource -> fatherResource.getId().equals(nowResource.getParentId()))
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(childenResourceList)) {
            return null;
        }

        childenResourceList.stream()
                .filter(childenResource -> {
                    long count = resourceTree.stream()
                            .filter(resourceTreeSub -> childenResource.getId().equals(resourceTreeSub.getId()))
                            .count();
                    if (count > 0) {
                        return false;
                    }
                    return true;
                })
                .forEach(nowResource1 -> {
                    ResourceVO resourceVO = nowResource1.convert(ResourceVO.class);
                    List<ResourceVO> childrenMenuList = loopResourceTree(resourceVO, menuList);
                    resourceVO.setChildsResourceList(childrenMenuList);
                    resourceTree.add(resourceVO);
                });
        //根据 sort 属性排序


        return resourceTree.stream()
                .filter(resourceTreeSub -> resourceTreeSub.getSort() != null)
                .sorted(Comparator.comparing(ResourceVO::getSort))
                .collect(Collectors.toList());

    }

    /**
     * @Description 将 父级 resource 放入list中
     * @param all 所有 resource
     * @param parentId 父级resource id
     * @param link 将要放入的list
     * @return
     * @Author yjzhao
     * @Date 2019/9/18 11:11
     **/
    private static void putPraentResource(List<Resource> all, String parentId, LinkedList<Resource> link) {
        for(Resource r : all) {
            if(r.getId().equals(parentId)) {
                link.addFirst(r);
                if(!r.getParentId().equals("0")) {
                    putPraentResource(all, r.getParentId(), link);
                }
                return;
            }
        }
    }

}
